package gtug.duesseldorf.server;

import java.io.IOException;
import java.util.Collection;
import java.util.List;

import org.waveprotocol.wave.examples.fedone.waveclient.common.ClientBackend;
import org.waveprotocol.wave.examples.fedone.waveclient.common.ClientUtils;
import org.waveprotocol.wave.examples.fedone.waveclient.common.ClientWaveView;
import org.waveprotocol.wave.examples.fedone.waveclient.common.IndexEntry;
import org.waveprotocol.wave.examples.fedone.waveclient.common.WaveletOperationListener;
import org.waveprotocol.wave.examples.fedone.waveclient.console.ConsoleUtils;
import org.waveprotocol.wave.model.document.operation.BufferedDocOp;
import org.waveprotocol.wave.model.document.operation.DocOpComponentType;
import org.waveprotocol.wave.model.document.operation.impl.BufferedDocOpImpl;
import org.waveprotocol.wave.model.operation.wave.WaveletDocumentOperation;
import org.waveprotocol.wave.model.wave.ParticipantId;
import org.waveprotocol.wave.model.wave.data.WaveletData;

import gtug.duesseldorf.client.GreetingService;

import com.google.common.collect.Lists;
import com.google.gwt.dev.js.rhino.ObjToIntMap.Iterator;
import com.google.gwt.user.server.rpc.RemoteServiceServlet;

/**
 * The server side implementation of the RPC service.
 */
@SuppressWarnings("serial")
public class GreetingServiceImpl extends RemoteServiceServlet implements
		GreetingService {

	/** connection information **/
	//TODO: move these to config file
	private static String wave_host = "192.168.49.131";
	private static int wave_port = 9876;
	
	/** All document rendering and operations are done to a single document. */
	private static final String MAIN_DOCUMENT_ID = "main";

	private StringBuffer m_data = new StringBuffer();
	private ClientBackend m_backend;

	/** Open wave, to potentially render differently. */
	private ClientWaveView m_openWave = null;
	
	public GreetingServiceImpl() {
		super();
	}
	
	@Override
	public Boolean serverConnect(String name) {
		try {
			m_backend = new ClientBackend(name, wave_host, wave_port);
			
		} catch (IOException e) {
			e.printStackTrace();
			
			return Boolean.FALSE;
		}
		

//		BufferedDocOpImpl waveData = (BufferedDocOpImpl)c.toArray()[0];
//		if (isConnected() == false) {
//		throwNotConnected();
//	}

//	List<IndexEntry> index = ClientUtils.getIndexEntries(m_backend.getIndexWave());
//	addData("here");
//	for(int i = 0; i<index.size(); i++) {
//		//setOpenWave(m_backend.getWave(index.get(entry).getWaveId()));
//		addData("-> " + index.get(i).toString());
//	}
	
		
		
		
		m_backend.addWaveletOperationListener(new WaveletOperationListener() {
			
			@Override
			public void waveletDocumentUpdated(String author,
					WaveletData wavelet, WaveletDocumentOperation docOp) {

				StringBuffer buf = new StringBuffer();
				buf.append("---");
				buf.append("waveDocUpdate");
				buf.append("\nauthor: ").append(author);
				buf.append("\nwavelet: ").append(wavelet);
				buf.append("\ndocOp: ").append(docOp);

//					addData(buf.toString());
//					addData("-> " + docOp);
				
//					addData("-> " + docOp.getOperation().getCharactersString(0));
			}

			@Override
			public void participantRemoved(String author, WaveletData wavelet,
					ParticipantId participantId) {

				StringBuffer buf = new StringBuffer();

				buf.append("---");
				buf.append("participantRemoved");
				buf.append("author: " + author);
				buf.append("wavelet: " + wavelet);
				buf.append("partID: " + participantId);

//					addData(buf.toString());
			}

			@Override
			public void participantAdded(String author, WaveletData wavelet,
					ParticipantId participantId) {

				StringBuffer buf = new StringBuffer();
				buf.append("---");
				buf.append("partAdd");
				buf.append("author: " + author);
				buf.append("wavelet: " + wavelet);
				buf.append("partId: " + participantId);

//					addData(buf.toString());
			}

			@Override
			public void onDeltaSequenceStart(WaveletData wavelet) {

				StringBuffer buf = new StringBuffer();
				buf.append("---");
				buf.append("onDeltaSeqStart");
				buf.append("wavelet: " + wavelet);

//					addData(buf.toString());
				
			}

			@Override
			public void onDeltaSequenceEnd(WaveletData wavelet) {

				StringBuffer buf = new StringBuffer();
				buf.append("---");
				buf.append("onDeltaSeqEnd");
				buf.append("wavelet: " + wavelet);

				
				

				
				
				
//					addData(buf.toString());
				
//					addData(wavelet.getDocuments().size() + "\n" );
				
//					addData(wavelet.getDocuments().toString());
				
//					addData(wavelet.getDocuments().size() +"");
				
				Collection c = wavelet.getDocuments().values();
//					addData(c.toString());
				
				
				BufferedDocOpImpl waveData = (BufferedDocOpImpl)c.toArray()[0]; 
				
				String lastElement = "";
				boolean foundLine;
				
				for (int i=0; i<waveData.size(); i++) {
					foundLine = false;
					
					DocOpComponentType type = waveData.getType(i);
					if (type == DocOpComponentType.ELEMENT_START) {
//							addData("ELEMENT_START: " + waveData.getElementStartTag(i));
						
						if (waveData.getElementStartTag(i).equals("line")) {
							foundLine = true;
						}
						
//						} else if (type == DocOpComponentType.ELEMENT_END) {
						
					} else if (type == DocOpComponentType.CHARACTERS) {
//							if (true) {
//								lastElement = waveData.getCharactersString(i);
//							}
						
						addData("getCharactersString -> " + waveData.getCharactersString(i) + "\n");
						addData("\n");
					}
						
						
				}
				
				// commit last string element
//					addData(lastElement);
				
			}

			@Override
			public void noOp(String author, WaveletData wavelet) {

				StringBuffer buf = new StringBuffer();
				buf.append("---");
				buf.append("noOp");
				buf.append("author: " + author);
				buf.append("wavelet: " + wavelet);

//					addData(buf.toString());
			}
		});
		
		return Boolean.TRUE;
	}


	@Override
	public Boolean serverDisconnect() {
		
		if(m_backend != null){
			m_backend.shutdown();
		}
		m_backend = null;
		
		return Boolean.TRUE;
	}

	@Override
	public String[] waveListing() {
		
		// get wave list
		List<IndexEntry> index = ClientUtils.getIndexEntries(m_backend.getIndexWave());
		
		// set size 
		String[] listing = new String[index.size()];
		
		for (int i=0; i < index.size(); i++) {
			listing[i] = index.get(i).getWaveId().toString();
//			listing[i] = listing[i].replace("[WaveId:", "");
//			listing[i] = listing[i].replace("]", "");
		}

		return listing;
	}

	@Override
	public String waveNew(String name) {
		
		
		m_backend.createConversationWave();
		
		return "waveNew";
	}

	@Override
	public String waveOpen(String name) {
//		try {
//			openWave(0);
//		} catch (Exception e) {
//			// TODO Auto-generated catch block
//			e.printStackTrace();
//		}
		return name;
		
	}

		
	private void openWave(int entry) throws Exception {
//		if (isConnected() == false) {
//			throwNotConnected();
//		}
//
//		List<IndexEntry> index = ClientUtils.getIndexEntries(m_backend.getIndexWave());
//
//		if (entry >= index.size()) {
//			System.out.print("Error: entry is out of range, ");
//			if (index.isEmpty()) {
//				System.out
//						.println("there are no available waves (try \"/new\")");
//			} else {
//				System.out.println("expecting [0.." + (index.size() - 1)
//						+ "] (for example, \"/open 0\")");
//			}
//		} else {
//			setOpenWave(m_backend.getWave(index.get(entry).getWaveId()));
//		}

	}
	
	/**
	 * Set a wave as the open wave.
	 * 
	 * @param wave
	 *            to set as open
	 */
	private void setOpenWave(ClientWaveView wave) {
//		if (ClientUtils.getConversationRoot(wave) == null) {
//			wave.createWavelet(ClientUtils.getConversationRootId(wave));
//		}
//
//		m_openWave = wave;
	}
	
	/**
	 * @return the open wavelet of the open wave, or null if no wave is open
	 */
	private WaveletData getOpenWavelet() {
		return (m_openWave == null) ? null : ClientUtils
				.getConversationRoot(m_openWave);
	}

	/**
	 * @return open document, or null if no wave is open or main document
	 *         doesn't exist
	 */
	private BufferedDocOp getOpenDocument() {
		return getOpenWavelet() == null ? null : getOpenWavelet()
				.getDocuments().get(MAIN_DOCUMENT_ID);
	}

	/**
	 * @return whether the client is connected to any server
	 */
	private boolean isConnected() {
		return m_backend != null;
	}
	
	/**
	 * Print error message if user is not connected to a server.
	 * 
	 * @throws Exception
	 */
	private void throwNotConnected() throws Exception {
		throw new Exception("Error: not connected, run \"/connect user@domain server port\"");
	}
	

	private void renderInbox() {
		List<String> inbox = getInbox();
		StringBuffer buf = new StringBuffer();
		for (String s : inbox) {
			buf.append(s).append("\n");
		}
	
		System.out.println(buf.toString());
	}	
	
	/**
	 * get inbox-entries
	 * 
	 * @return
	 */
	public List<String> getInbox() {
		List<IndexEntry> indexEntries = ClientUtils.getIndexEntries(m_backend
				.getIndexWave());
		List<String> lines = Lists.newArrayList();

		for (int i = 0; i < indexEntries.size(); i++) {
			ClientWaveView wave = m_backend.getWave(indexEntries.get(i)
					.getWaveId());
			StringBuilder line = new StringBuilder();

			if ((wave == null)
					|| (ClientUtils.getConversationRoot(wave) == null)) {
				line.append("...");
			} else {
				// HashedVersion version = wave.getWaveletVersion(ClientUtils
				// .getConversationRootId(wave));

				line.append(String.format("%4d) ", i));
				line.append(String.format("(%s) ", wave.getWaveId().getId()));
				line.append(ConsoleUtils.renderNice(indexEntries.get(i)
						.getDigest()));
				
				// GSE: add this later to inform user about updates waves

				// if (wave == openWave) {
				// ansiCodes.add(ConsoleUtils.ANSI_BLUE_BG);
				// ansiCodes.add(ConsoleUtils.ANSI_WHITE_FG);
				// lastSeenVersions.put(wave, version);
				// } else if (!version.equals(lastSeenVersions.get(wave))) {
				// ansiCodes.add(ConsoleUtils.ANSI_BOLD);
				// }
			}
		}
		return lines;
	}
	
	@Override
	public String waveQuit(String name) {
		// TODO Auto-generated method stub
		return "waveQuit";
	}

	@Override
	public String waveGetMessage(String name) {
		return getData();
	}
	
	private synchronized void addData(String data) {
		m_data.append(data);
	}

	public synchronized String getData() {
		String s = m_data.toString();
		m_data.setLength(0);

		return s;
	}
	
}